﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Suzsoft.Smart.Data;
using ESOL.Common.Entity;
using BPM.Base.Tools;
using System.Threading;
using System.Data;

namespace ESOL.Business.Prod
{
   public class PPSchedule
    {
      private ProdPlanOrderEntity pp;
      private List<BomTechnicItemEntity> prodProcs;
      private List<BomTechnicItemMtrlEntity> procMtrls;
      private List<ProdWoEntity> wos;
      private ProdWoEntity currentWo;
      private ProdWoEntity mainWo;
      private List<ProdWoEntity> subWos;
      public List<ProdWoTaskDetailEntity> ProductPlanSchedule = new List<ProdWoTaskDetailEntity>();
      public  int TheardCount = 0;

      public PPSchedule(string ppId)
      {
          InitPP(ppId);
      }


       //初始化生产
      public void InitPP(string ppId)
      {
          pp = DataAccess.SelectSingle<ProdPlanOrderEntity>(new ProdPlanOrderEntity { Id=ppId});

          //初始化生产计划所包括的工单
          wos = DataAccess.Select<ProdWoEntity>(new ProdWoEntity { PoId = pp.Id });

          //初始化子工单，即所有半成品工单。在成品工单排产时，确认成品工单的物料是否准备好时要用到。
          #region 初始化子工单
          subWos =new  List<ProdWoEntity>();
          foreach (ProdWoEntity wo in wos)
          {
              if (!string.IsNullOrEmpty(wo.ParentWoNo))
              {
                  int index = subWos.FindIndex(delegate(ProdWoEntity p) { return p.WoNo == wo.ParentWoNo; });
                  if (index >= 0)
                      subWos.Insert(index, wo);
                  else
                      subWos.Add(wo);
              }
          }
          #endregion
      }



       //排列计划中所有工单的优先级，子工单优先级高于父工单
      public List<ProdWoEntity> GetScheduleWoPriority()
      {
          List<ProdWoEntity> woList = new List<ProdWoEntity>();
          ProdWoEntity rootWo =null;
          foreach (ProdWoEntity wo in wos)
          {
              if (!string.IsNullOrEmpty(wo.ParentWoNo))
              {
                  int index = woList.FindIndex(delegate(ProdWoEntity p) { return p.WoNo == wo.ParentWoNo; });
                  if (index >= 0)
                      woList.Insert(index, wo);
                  else
                      woList.Add(wo);
              }
              if (string.IsNullOrEmpty(wo.ParentWoNo))
                  rootWo = wo;
          }
          if (rootWo != null) woList.Add(rootWo);
          return woList;
      }


      //按计划排产
      public List<ProdWoTaskDetailEntity> SchedulePP(List<ScheduleInput> input, string SchType)
      {
          List<ProdWoTaskDetailEntity> scheduleOutput = new List<ProdWoTaskDetailEntity>();
          DateTime planStartTime = pp.StartTime;
          List<string> waitingWoList = new List<string>();
          //排工单优先级

          foreach (ProdWoEntity wo in wos)
          {
              if (!string.IsNullOrEmpty(wo.ParentWoNo))
              {
                 int index= subWos.FindIndex(delegate(ProdWoEntity p) { return p.WoNo == wo.ParentWoNo; });
                 if (index >= 0)
                     subWos.Insert(index, wo);
                 else
                     subWos.Add(wo);
              }
              if (string.IsNullOrEmpty(wo.ParentWoNo))
                  mainWo = wo;
              waitingWoList.Add(wo.WoNo);
              //save();
              //clear
          }
          if (subWos != null && subWos.Count > 0)
          {
              foreach (ProdWoEntity wo in subWos)
              {
                  waitingWoList.Remove(wo.WoNo);
                  ScheduleWo(input, SchType, wo, waitingWoList);
              }
          }
          if (mainWo != null) ScheduleWo(input, SchType, mainWo, waitingWoList);
          return null;
      }


       //给定工单列表，依次排产
      public List<ProdWoTaskDetailEntity> ScheduleWoList(List<ProdWoEntity> woList,List<ScheduleInput> input, string SchType)
      {
          List<ProdWoTaskDetailEntity> scheduleOutput = new List<ProdWoTaskDetailEntity>();
          List<string> waitingWoList = new List<string>();

          foreach (ProdWoEntity wo in woList)
          {
              waitingWoList.Add(wo.WoNo);
          }

          if (woList != null && woList.Count > 0)
          {
              foreach (ProdWoEntity wo in woList)
              {
                  waitingWoList.Remove(wo.WoNo);
                  ScheduleWo(input, SchType, wo, waitingWoList);
              }
          }
          return null;
      }


       //给指定的工单排程。
      public string ScheduleWo(List<ScheduleInput> input, string SchType, ProdWoEntity wo,List<string> waitingWoList)
      {
          try
          {
              List<ProdWoTaskDetailEntity> scheduleOutput = new List<ProdWoTaskDetailEntity>();
              pp = DataAccess.SelectSingle<ProdPlanOrderEntity>(new ProdPlanOrderEntity { Id = wo.PoId });

              DateTime planStartTime = pp.StartTime;
              currentWo = wo;
              prodProcs = DataAccess.Select<BomTechnicItemEntity>(new BomTechnicItemEntity { BomTechnicId = wo.BomTechnicId });
              if (prodProcs != null && prodProcs.Count > 0) prodProcs = prodProcs.OrderBy(p => p.Sequence).ToList();

              procMtrls = DataAccess.Select<BomTechnicItemMtrlEntity>(new BomTechnicItemMtrlEntity { BomTechnicId = wo.BomTechnicId });

              if (wo.PlanStartDate != DateTime.MinValue && wo.PlanStartDate != DateTime.MaxValue)
                  planStartTime = wo.PlanStartDate;
              else
                  planStartTime = pp.StartTime;
            
              for (int prodIndex = 1; prodIndex <= wo.Qty; prodIndex++)
              {
                  ChildThreadException = null;
                  ChildThreadException += new ChildThreadExceptionHandler(Import_ChildThreadException); 
                  ProductSchedule prodSch = new ProductSchedule(this, wo, prodIndex, pp.StartTime, input);
                  prodSch.WaitingScheduleWoList = waitingWoList;
                  //Thread thread1 = new Thread(new ThreadStart(prodSch.ScheduleProduct));
                  //thread1.Start();
                  TheardCount++;
                  ThreadPool.QueueUserWorkItem(new WaitCallback(prodSch.ScheduleProduct), prodIndex);
              }

              // 等所有子线程执行完

              //while (TheardCount > 0)
              //{
              //    if (!string.IsNullOrEmpty(ErrorMessage)) throw (new Exception(ErrorMessage));
              //    Thread.Sleep(500);
              //}

              int maxWorkerThreads, workerThreads;
              int portThreads;
              DateTime startTime = DateTime.Now;
              TimeSpan ts = DateTime.Now - startTime;
              while (ts.TotalMilliseconds < 500 || TheardCount>0)
              {
                  /*
                   GetAvailableThreads()：检索由 GetMaxThreads 返回的线程池线程的最大数目和当前活动数目之间的差值。
                   而GetMaxThreads 检索可以同时处于活动状态的线程池请求的数目。
                   通过最大数目减可用数目就可以得到当前活动线程的数目，如果为零，那就说明没有活动线程，说明所有线程运行完毕。
                   */
                  if (!string.IsNullOrEmpty(ErrorMessage)) throw (new Exception(ErrorMessage));
                  if (ts.TotalMilliseconds >= 500)
                      startTime = DateTime.Now;
                  ThreadPool.GetMaxThreads(out maxWorkerThreads, out portThreads);
                  ThreadPool.GetAvailableThreads(out workerThreads, out portThreads);
                  if (maxWorkerThreads - workerThreads == 0)
                  {
                      Console.WriteLine("Thread Finished!");
                      break;
                  }
                  ts = DateTime.Now - startTime;
              }

              SaveTaskDetail();

         
             
              return "";
          }
          catch (Exception ex)
          {
              MyDebug.write(ex.Message);
              return ex.Message;
          }
      }



      //上一道工序结束时间=baseQty个产品上一道工序的结束时间，因为baseQty为移转基数
      public DateTime GetPreProcET(BomTechnicItemEntity preProc, int prodIndex)
      {
          int baseQty = preProc.BaseQty;
          int MaxTransLots = ((prodIndex-1) / baseQty + 1) * baseQty;
          if (MaxTransLots > pp.Qty) MaxTransLots =(int) pp.Qty;
          int MinTransLots = ((prodIndex-1) / baseQty) * baseQty;
          DateTime maxEndTime = DateTime.MinValue;
          List<ProdWoTaskDetailEntity> sch = ProductPlanSchedule.FindAll(delegate(ProdWoTaskDetailEntity o) { return o.WoNo == currentWo.WoNo && o.Sequence == preProc.Sequence && o.ProdIndex == MaxTransLots; });
          if (sch != null && sch.Count>0)
          {
             // sch = ProductPlanSchedule.FindAll(delegate(ScheduleOutput o) { return o.WoNo == currentWo.WoNo && o.Sequence == preProc.Sequence && o.prodIndex <= MaxTransLots && o.prodIndex > MinTransLots; });
              foreach (ProdWoTaskDetailEntity o in sch)
              {
                  if (o.EndTime > maxEndTime)
                      maxEndTime = o.EndTime;
              }
              return maxEndTime;
          }
          else
              return DateTime.MinValue;
      }


      //反回工作组已被安排的工单任务数
      public int GetWorkGroupAssignedTask(string woNo, int sequence, string workgroupId)
      {
          lock (ProductPlanSchedule)
          {
              var tasks = ProductPlanSchedule.FindAll(delegate(ProdWoTaskDetailEntity s) { return s.WoNo == woNo && s.Sequence == sequence && s.WorkgroupNo == workgroupId; }).Distinct();
              if (tasks != null && tasks.Count() > 0)
                 return tasks.Select(t => t.ProdIndex).Distinct().Count();
              else
                  return 0;
          }
      }


       //取已分配给指定工作组的任务数，供子线程调用
      public int GetAsingedQtyOfWorkGroup(string workgroupId)
      {
          lock (ProductPlanSchedule)
          {
              return ProductPlanSchedule.Where(t => t.WorkgroupNo == workgroupId).Select(t => t.ProdIndex).Distinct().Count();
          }
      }

       
      private delegate void AddDelegate(List<ProdWoTaskDetailEntity> schedule);
      //子线程的排程结果反馈到主线程中
      public bool AddRange(List<ProdWoTaskDetailEntity> schedule)
      {
          lock(ProductPlanSchedule)
          {
              ProductPlanSchedule.AddRange(schedule);
          }
          return true;
        
      }


       //取指定的车间日历内已分配给该工作组的任务，以确定工作组已被占用的时间
      public List<ProdWoTaskDetailEntity> GetAssignedTaskInCalendar(string workGroupId, DateTime startTime, DateTime endTime)
      {
          lock (ProductPlanSchedule)
          {
              return (from c in ProductPlanSchedule where c.WorkgroupNo == workGroupId && ((c.StartTime >= startTime && c.EndTime < endTime) || (c.EndTime > startTime && c.EndTime <= endTime)) select c).ToList().OrderBy(t => t.EndTime).ToList();
          }
      }
     


       //前一产品指定工序的完工时间
       public DateTime GetPreProdEndTime(string woNo,int sequence, int preProdIndex)
       {
           DateTime preProdEndTime = DateTime.MinValue;
           lock (ProductPlanSchedule)
           {
               var obj = (from c in ProductPlanSchedule where  c.WoNo == woNo && c.ProdIndex == preProdIndex && c.Sequence==sequence select c).ToList().OrderByDescending(t => t.EndTime).ToList();
               if (obj != null && obj.Count>0)
                   preProdEndTime = obj[0].EndTime;

           }
           return preProdEndTime;
       }



       public DateTime GetPreProdEndTimeOfWorkgroup(string woNo, int sequence, string workGroupId, int prodIndex)
       {
           DateTime preProdEndTime = DateTime.MinValue;
           lock (ProductPlanSchedule)
           {
               var obj = (from c in ProductPlanSchedule where c.WorkgroupNo == workGroupId && c.WoNo == woNo && c.ProdIndex < prodIndex && c.Sequence == sequence select c).ToList().OrderByDescending(t => t.EndTime).ToList();
               if (obj != null && obj.Count>0)
                   preProdEndTime = obj[0].EndTime;

           }
           return preProdEndTime;
       }


       //计算工单的物料是否准备好。
       public DateTime GetMtrlReadyTime(int prodIndex,BomTechnicItemEntity proc)
       {
           DateTime readyTime = DateTime.MinValue;
           List<BomTechnicItemMtrlEntity> tempProcMtrls= procMtrls.FindAll(delegate(BomTechnicItemMtrlEntity t) { return t.BomTechnicItemId == proc.Id; });
           if (tempProcMtrls != null && tempProcMtrls.Count > 0)
           {
               foreach (BomTechnicItemMtrlEntity mtrl in tempProcMtrls)
               {
                   ProdWoEntity subWo = subWos.Find(delegate(ProdWoEntity w) { return w.ProductId == mtrl.MtrlId; });
                   if (subWo != null)
                   {
                       //取subWo 第prodIndex* reqQty 个产品的结束时间
                       int reqProdQty = Convert.ToInt32(prodIndex * mtrl.ReqQty);
                       List<ProdWoTaskDetailEntity> schs = ProductPlanSchedule.FindAll(delegate(ProdWoTaskDetailEntity o) { return o.WoNo == subWo.WoNo && o.ProdIndex == reqProdQty; });
                       schs = DataAccess.Select<ProdWoTaskDetailEntity>(new ProdWoTaskDetailEntity { WoNo = subWo.WoNo, ProdIndex = reqProdQty });
                       foreach (ProdWoTaskDetailEntity sch in schs)
                       {
                           if (sch.EndTime > readyTime) readyTime = sch.EndTime;
                       }
                   }

               }
           }
           return readyTime;
       }


       //保存排程明细记录
       public string SaveTaskDetail()
       {
           //本次排程的所有工单
           List<string> deleteWo = new List<string>();
           deleteWo = ProductPlanSchedule.Select(t => t.WoNo).Distinct().ToList();
       
           List<ProdWoTaskDetailEntity> deleteTaskDetail = new List<ProdWoTaskDetailEntity>();
           foreach (string wo in deleteWo)
           {
               //删除数据库中已存在的该工单的排程记录
               var td = DataAccess.Select<ProdWoTaskDetailEntity>(new ProdWoTaskDetailEntity { WoNo = wo });
               deleteTaskDetail.AddRange(td);
           }

           using (DataAccessBroker broker = DataAccessFactory.Instance())
           {
               try
               {
                   broker.BeginTransaction();
                   if (deleteTaskDetail != null && deleteTaskDetail.Count > 0)
                       DataAccess.Delete(deleteTaskDetail, broker);
                   DataAccess.Insert(ProductPlanSchedule, broker);
                   broker.Commit();
                   return "";
               }
               catch (Exception ex)
               {
                   broker.RollBack();
                   throw ex;
               }
           }
       }


       public static List<ProdWoTaskEntity> GetWoTaskFromTaskDetail(string woNo)
       {
           //汇总成prod_wo_task
           List<ProdWoTaskEntity> tasks = new List<ProdWoTaskEntity>();
           List<ProdWoTaskDetailEntity> taskDetails = DataAccess.Select<ProdWoTaskDetailEntity>(new ProdWoTaskDetailEntity { WoNo = woNo });
           foreach (ProdWoTaskDetailEntity output in taskDetails)
           {
               ProdWoTaskEntity cTask = tasks.Find(delegate(ProdWoTaskEntity t) { return t.WoId == output.WoNo && t.Sequence == output.Sequence && t.WorkgroupNo == output.WorkgroupNo; });
               if (cTask != null)
               {
                   if (output.StartTime < cTask.ReqTimeFrom)
                       cTask.ReqTimeFrom = output.StartTime;
                   if (output.EndTime > cTask.ReqTimeTo)
                       cTask.ReqTimeTo = output.EndTime;
               }
               else
               {
                   cTask = new ProdWoTaskEntity();
                   cTask.Id = Guid.NewGuid().ToString();
                   cTask.ReqTimeFrom = output.StartTime;
                   cTask.ReqTimeTo = output.EndTime;
                   cTask.Sequence = output.Sequence;
                   cTask.TechnicId = output.TechnicId;
                   cTask.WoId = output.WoNo;
                   cTask.WorkgroupNo = output.WorkgroupNo;
                   tasks.Add(cTask);
               }
           }


           //更新req_qty 
           foreach (ProdWoTaskEntity task in tasks)
           {
               int cout = taskDetails.Where(t => t.WoNo == task.WoId && t.WorkgroupNo == task.WorkgroupNo && t.Sequence == task.Sequence).Select(t => t.ProdIndex).Distinct().Count();
               task.ReqQty = cout;
           }

           return tasks;
       }


       //保存工作组计划。
       public  static string SaveWoTask(List<string> woList)
       {
           try
           {
               List<ProdWoTaskEntity> deleteTask = new List<ProdWoTaskEntity>();
               List<ProdWoTaskEntity> insertTask = new List<ProdWoTaskEntity>();
               List<ProdWoEntity> woEntityList = new List<ProdWoEntity>();
               foreach (string wo in woList)
               {
                   List<ProdWoTaskEntity> tasks = GetWoTaskFromTaskDetail(wo);
                   if (tasks == null || tasks.Count == 0) continue;
                   insertTask.AddRange(tasks);

                   //删除数据库中已存在的该工单的排程记录
                   var ts = DataAccess.Select<ProdWoTaskEntity>(new ProdWoTaskEntity { WoId = wo });
                   deleteTask.AddRange(ts);

                   //对已经开工过的排程记录，保留它的开工时间，和完工数。
                   #region
                   foreach (ProdWoTaskEntity wotask in deleteTask)
                   {
                       if (!string.IsNullOrEmpty(wotask.Status) && wotask.Status.Equals("RUN"))
                       {
                           ProdWoTaskEntity cTask = insertTask.Find(delegate(ProdWoTaskEntity t) { return t.WoId == wotask.WoId && t.Sequence == wotask.Sequence && t.WorkgroupNo == wotask.WorkgroupNo; });
                           if (cTask.ReqTimeFrom > wotask.ReqTimeFrom)
                               cTask.ReqTimeFrom = wotask.ReqTimeFrom;

                           //工作组已完成所有任务，可能需要改变完工时间。
                           if (wotask.ReqQty == wotask.ReportedQty && cTask.ReqQty == wotask.ReqQty)
                               if (cTask.ReqTimeTo > cTask.ReqTimeTo)
                                   cTask.ReqTimeTo = cTask.ReqTimeTo;
                           cTask.Status = "RUN";
                           cTask.ReportedQty = wotask.ReportedQty;
                           cTask.ReportedDate = wotask.ReportedDate;
                           cTask.ReportedBy = wotask.ReportedBy;
                       }
                   }
                   #endregion
                   //更新工单讫始时间
                   #region 更新工单讫始时间
                   DateTime datStartTime = DateTime.MaxValue;
                   DateTime datEndTime = DateTime.MinValue;
                   ProdWoEntity woEntity = DataAccess.SelectSingle<ProdWoEntity>(new ProdWoEntity { WoNo = wo });
                   List<ProdWoTaskEntity> woTaskList = insertTask.Where(t => t.WoId == wo).ToList();
                   foreach (ProdWoTaskEntity wotask in woTaskList)
                   {
                       if (wotask.ReqTimeFrom < datStartTime) datStartTime = wotask.ReqTimeFrom;
                       if (wotask.ReqTimeTo > datEndTime) datEndTime = wotask.ReqTimeTo;
                   }
                   if (datStartTime < DateTime.MaxValue) woEntity.PlanStartDate = datStartTime;
                   if (datEndTime > DateTime.MinValue) woEntity.ReqDate = datEndTime;
                   woEntityList.Add(woEntity);
                   #endregion
               }

                #region SaveByTransaction
               using (DataAccessBroker broker = DataAccessFactory.Instance())
               {
                   try
                   {
                       broker.BeginTransaction();

                       if (deleteTask != null && deleteTask.Count > 0)
                           DataAccess.Delete(deleteTask, broker);
                       DataAccess.Insert(insertTask, broker);
                       DataAccess.Update(woEntityList, broker);
                       broker.Commit();
                       return "";
                   }
                   catch (Exception ex)
                   {
                       broker.RollBack();
                       throw ex;
                   }
               }

                #endregion

               return "";
           }
           catch (Exception ex)
           {
               return ex.Message;
           }
       }



        #region 捕获子线程异常
       private delegate void ChildThreadExceptionHandler(string message);
       private event ChildThreadExceptionHandler ChildThreadException;
       private string ErrorMessage = "";
       
       public virtual void OnChildThreadException(string message)
       {
           if (ChildThreadException != null)
               ChildThreadException(message);
       }

       public void Import_ChildThreadException(string message)
       {
           ErrorMessage = message;
       }
        #endregion
    }


    public class ProductSchedule
    {
        private List<BomTechnicItemEntity> prodProcs;
        private List<BomTechnicItemMtrlEntity> procMtrls;
        private ProdWoEntity currentWo;

        private int prodIndex ;
        private List<ScheduleInput> input ;
        private DateTime planStartTime;
        private PPSchedule ppSchedule;
        public List<string> WaitingScheduleWoList { get; set; }//本次待排程的工单号列表。
       public ProductSchedule(PPSchedule ppSchedule,ProdWoEntity wo,int pIndex,DateTime plant_st, List<ScheduleInput> input)
       {
           this.ppSchedule = ppSchedule;
           this.currentWo=wo;
           this.prodIndex = pIndex;
           this.planStartTime=plant_st;
           this.input=input;
            prodProcs = DataAccess.Select<BomTechnicItemEntity>(new BomTechnicItemEntity { BomTechnicId = wo.BomTechnicId });
            if (prodProcs != null && prodProcs.Count > 0) prodProcs = prodProcs.OrderBy(p => p.Sequence).ToList();

            procMtrls = DataAccess.Select<BomTechnicItemMtrlEntity>(new BomTechnicItemMtrlEntity { BomTechnicId = wo.BomTechnicId });
       }
        
       //计算一个产品的排产情况
      public void ScheduleProduct(object i)
      {
          try
          {
            //  throw (new Exception("text"));

              DateTime startTime = planStartTime;
              foreach (BomTechnicItemEntity proc in prodProcs)
              {
                  if(proc.RateTime==0)
                      throw (new Exception("工序" + proc.TechnicName + ", 没有指定有效的工序用时"));

               
                  DateTime datMintimeStartProc = MinTimeStartTheProc(prodIndex, proc);
                  if (datMintimeStartProc < startTime) datMintimeStartProc = startTime;
           
                  //分配哪个工作组要完成这道工序
                  ProdWorkgroupEntity workgroup = WhichWorkgroupDoTheProc(input, proc, prodIndex, ref datMintimeStartProc);
                  if (workgroup == null)
                      throw (new Exception("没有工作组完成" + proc.TechnicName + "工序"));

           
                  //生成工艺用料需求时刻表
                  //  GetProcMtrls(proc,workgroup,prodIndex);

                  //产生计划安排
              }

              ppSchedule.TheardCount--;
              
          }
          catch (Exception ex)
          {
              ppSchedule.OnChildThreadException(ex.Message);
          }
              
          
      }


       //工序最早开工时间
      public DateTime MinTimeStartTheProc(int prodIndex, BomTechnicItemEntity proc)
      {
          DateTime datPreProcET=DateTime.MinValue; //上道工序的结束时间
          DateTime datMtrlReadyTime;//物料准备好时间
          DateTime datStartTime;//本工序开工时间

          //取上一道工序
          BomTechnicItemEntity preProc = GetPreProc(proc);

          //上一道工序结束时间
          if (preProc != null)
          {
              datPreProcET = ppSchedule.GetPreProcET(preProc, prodIndex);
              while (datPreProcET == DateTime.MinValue)
              {
                  Thread.Sleep(50);
                  datPreProcET = ppSchedule.GetPreProcET(preProc, prodIndex);
              }
          }
          if (datPreProcET > DateTime.MinValue) datPreProcET = datPreProcET.AddMinutes((int)preProc.CloseTime);

          
          //物料准备好时间
          datMtrlReadyTime = ppSchedule.GetMtrlReadyTime(prodIndex, proc);

          return datMtrlReadyTime > datPreProcET ? datMtrlReadyTime : datPreProcET;
      }


       //取上一道工序
      public BomTechnicItemEntity GetPreProc(BomTechnicItemEntity proc)
      {
          BomTechnicItemEntity preProc=null;
          int index = prodProcs.FindIndex(0, prodProcs.Count, delegate(BomTechnicItemEntity p) { return p.Id == proc.Id; });
        if (index > 0) preProc = prodProcs[index - 1];
        return preProc;    
      }


     
      public ProdWorkgroupEntity WhichWorkgroupDoTheProc(List<ScheduleInput> input ,BomTechnicItemEntity proc , int prodindex,ref DateTime startTime)
      {
          ProdWorkgroupEntity selectedWorkgroup = null;
          DateTime workgroupStartTime = startTime;
          List<ScheduleInput> assignTask=null;
          ScheduleInput assignedTask = null;//有没有事先指定的工作组
          List<ProdWoTaskDetailEntity> schForEachWorkGroup = new List<ProdWoTaskDetailEntity>();
          //有没有指定哪个工作组完成
          if(input!=null)
          assignTask= input.FindAll(delegate (ScheduleInput s ){return s.WoNo==currentWo.WoNo  && s.Sequence==proc.Sequence;});
         

          #region 有没有指定哪个工作组完成本工序
          if (assignTask != null && assignTask.Count>0)
          {
              //让前一产品的本个工序先计算
              if (prodindex > 1)
              {
                  DateTime preProdEndTime = ppSchedule.GetPreProdEndTime(currentWo.WoNo, proc.Sequence, prodindex - 1);
                  while (preProdEndTime == DateTime.MinValue)
                  {
                      Thread.Sleep(50);
                      preProdEndTime = ppSchedule.GetPreProdEndTime(currentWo.WoNo, proc.Sequence, prodindex - 1);
                  }
              }

              foreach (ScheduleInput assigned in assignTask)
              {
                  //工作组已被安排的工作任务数
                  var taskedCount = ppSchedule.GetWorkGroupAssignedTask(currentWo.WoNo, proc.Sequence, assigned.WorkGroupId);
                  if (assigned.Qty > taskedCount)
                  {
                      assignedTask = assigned;
                      break;
                  }
              }// end foreach (

          }
          #endregion
          if (assignedTask != null)
          {//有手工指定的工作组可以完成该工序
              if (workgroupStartTime < assignedTask.StartDate)
                  workgroupStartTime = assignedTask.StartDate;
            selectedWorkgroup=  DataAccess.SelectSingle<ProdWorkgroupEntity>(new ProdWorkgroupEntity { Id = assignedTask.WorkGroupId });
            startTime = workgroupStartTime;

          
           
            //本工作组做上一个产品的结束时间。
            DateTime preProdEndTimeOfWorkGroup = ppSchedule.GetPreProdEndTimeOfWorkgroup(currentWo.WoNo, proc.Sequence, assignedTask.WorkGroupId, prodindex);

            if (startTime < preProdEndTimeOfWorkGroup)
                workgroupStartTime = preProdEndTimeOfWorkGroup;

            AssignTheProcTime(assignedTask.WorkGroupId, workgroupStartTime, currentWo.ReqDate, proc, prodindex, ref schForEachWorkGroup);
            ppSchedule.AddRange(schForEachWorkGroup);
          }
          else
          {
              //取可以完成工序的工作组列表
              List<ProdWorkgroupEntity> workgroups = DataAccess.Select<ProdWorkgroupEntity>(new ProdWorkgroupEntity { StationType = proc.StationType }).OrderBy(t=>t.WorkgroupName).ToList();
             if(workgroups==null || workgroups.Count<=0)
                 throw (new Exception("找不到工作组可以做"+proc.TechnicName+" 工序！"));
              if (workgroups != null)
              {
                 
                  DateTime minTimeSelectedWorkgroupStartTheProc=DateTime.MaxValue;
                  foreach (ProdWorkgroupEntity workgroup in workgroups)
                  {
                      workgroupStartTime = startTime;
                     

                      #region 哪个工作组可以最先做完 
                     // Console.WriteLine("prodInex:" + prodIndex + " | proc:" + proc.Sequence + "workgroup:"+workgroup.WorkgroupName+" AssignTheProcTime Start");
                      //是否超出工作组计划数
                      List<ScheduleInput> workGroupInput = null;
                      string workgroupid = workgroup.Id;
                      if(input!=null) workGroupInput = (from c in input where c.WorkGroupId == workgroupid select c).ToList();
                      if (workGroupInput != null && workGroupInput.Count > 0)
                      {
                          int workGroupAssignedQty = (int)workGroupInput[0].Qty;
                          if (workGroupAssignedQty > 0)
                          {
                              int taskedCount = ppSchedule.GetWorkGroupAssignedTask(currentWo.WoNo, proc.Sequence, workgroupid);// (from c in ProductPlanSchedule where c.WorkGroupId == workgroupid select c.prodIndex).Distinct().Count();

                              if (taskedCount >= workGroupAssignedQty) //超出工作组计划数
                                  continue;
                              
                          }
                      }

                      //让前一产品的本个工序先计算
                      if (prodindex > 1)
                      {
                          DateTime preProdEndTime = ppSchedule.GetPreProdEndTime(currentWo.WoNo, proc.Sequence, prodindex-1);
                          while (preProdEndTime == DateTime.MinValue )
                          {
                              Thread.Sleep(50);
                              preProdEndTime = ppSchedule.GetPreProdEndTime(currentWo.WoNo, proc.Sequence, prodindex - 1);
                          }
                         
                      }

                     
                      //本工作组做上一个产品的结束时间。
                      DateTime preProdEndTimeOfWorkGroup = ppSchedule.GetPreProdEndTimeOfWorkgroup(currentWo.WoNo, proc.Sequence, workgroupid, prodindex);

                      if (startTime < preProdEndTimeOfWorkGroup)
                           workgroupStartTime = preProdEndTimeOfWorkGroup;
                     
                      AssignTheProcTime(workgroup.Id, workgroupStartTime, currentWo.ReqDate, proc, prodindex, ref schForEachWorkGroup);
                      Console.WriteLine("prodInex:" + prodIndex + " | proc:" + proc.Sequence + " AssignTheProcTime End");
                      #endregion
                  }//end foreach


                  //哪个工作组可以最先做完 
                  schForEachWorkGroup = schForEachWorkGroup.OrderBy(t => t.WorkgroupNo).ToList();
                  string selectGroupId = "";
                  Dictionary<string,DateTime> workGroupby=new  Dictionary<string,DateTime>(); //记录每个工作组的最终完工时间
                  foreach (ProdWoTaskDetailEntity sch in schForEachWorkGroup)
                  {
                      if (!sch.WorkgroupNo.Equals(selectGroupId))
                      {
                          workGroupby.Add(sch.WorkgroupNo, sch.EndTime);
                          selectGroupId = sch.WorkgroupNo;
                      }
                      else
                      {
                          if (sch.EndTime > workGroupby[selectGroupId])
                              workGroupby[selectGroupId] = sch.EndTime;
                      }
                    
                  }
                  
                  foreach (KeyValuePair<string, DateTime> kv in workGroupby)
                  {
                      if (kv.Value < minTimeSelectedWorkgroupStartTheProc)
                      {
                          selectGroupId = kv.Key;
                          minTimeSelectedWorkgroupStartTheProc = kv.Value;
                      }
                      else if (minTimeSelectedWorkgroupStartTheProc == kv.Value)
                      {//完工时间相同，分配给已分配数较少的工作组
                          int selectGroupTotalAssignedQty = ppSchedule. GetAsingedQtyOfWorkGroup(selectGroupId);// ProductPlanSchedule.Where(t => t.WorkGroupId == selectGroupId).Select(t => t.prodIndex).Distinct().Count();
                         string key = kv.Key;
                         int currentKeyTotalAssignedQty = ppSchedule.GetAsingedQtyOfWorkGroup(key); //ProductPlanSchedule.Where(t => t.WorkGroupId == key).Select(t => t.prodIndex).Distinct().Count();
                         if (selectGroupTotalAssignedQty > currentKeyTotalAssignedQty)
                         {
                             selectGroupId = kv.Key;
                             minTimeSelectedWorkgroupStartTheProc = kv.Value;
                         }
                      }
                     // ppSchedule.RemoveProcessWorkGroup(currentWo.WoNo,proc.Sequence,kv.Key, prodindex);
                  }

                  selectedWorkgroup = DataAccess.SelectSingle<ProdWorkgroupEntity>(new ProdWorkgroupEntity { Id = selectGroupId });
                  schForEachWorkGroup = schForEachWorkGroup.Where(t => t.WorkgroupNo == selectGroupId).ToList();
                  ppSchedule.AddRange(schForEachWorkGroup);
              }

          }
          return selectedWorkgroup;
      }



       //工作组可以完成该工序的最早时间
      public void AssignTheProcTime(string workGroupId, DateTime startTime, DateTime endTime, BomTechnicItemEntity proc, int prodIndex, ref List<ProdWoTaskDetailEntity> schForEachWorkGroup)
      {
          //在工作日历内安排工作组生产该工序
          //检查工序能否在计划时间内完成。
          
          //取可以做工序的日历
          List<SysWorkingPeriodEntity> calendars = null;
          string sql = "Select * from SYS_WORKING_PERIOD where time_to>'" + startTime + "' order by time_from";
          calendars=DataAccess.Select<SysWorkingPeriodEntity>(sql);
          if (calendars == null || calendars.Count == 0)
          {
              throw (new Exception("找不到符合条件的工作日历！"));
          }

          int nInterval = (int)proc.RateTime;
          SysWorkingPeriodEntity calendar = calendars[0];//最早可以开始的工作日历；

          SetWorkGroupInCalendar(calendar, workGroupId, startTime, endTime,ref nInterval, proc, prodIndex,ref schForEachWorkGroup);

      }

      public void SetWorkGroupInCalendar(SysWorkingPeriodEntity calendar, string workGroupId, DateTime startTime,DateTime plan_et,ref int interval,
          BomTechnicItemEntity proc, int prodIndex, ref List<ProdWoTaskDetailEntity> schForEachWorkGroup)
      {

          DateTime datProcStartTime;
          DateTime datProdEndTime;
          if (calendar.TimeFrom > startTime || startTime==DateTime.MinValue)
              datProcStartTime = calendar.TimeFrom;
          else
              datProcStartTime = startTime;

          if (calendar.TimeTo >= plan_et && (plan_et!=DateTime.MinValue && plan_et!=DateTime.MaxValue))
              datProdEndTime = plan_et;
          else
              datProdEndTime = calendar.TimeTo;

        
          AssignWorkGroups(workGroupId,datProcStartTime,datProdEndTime,ref interval,prodIndex,proc,ref schForEachWorkGroup);
          if (interval > 0)
          {
              //取下一个工作日历时间段
              List<SysWorkingPeriodEntity> calendars = null;
              string sql = "Select top 1 * from SYS_WORKING_PERIOD where time_from>'" + startTime + "' order by time_from";
             
              calendars = DataAccess.Select<SysWorkingPeriodEntity>(sql);
              if (calendars == null || calendars.Count == 0)
              {
                  throw (new Exception("找不到符合条件的工作日历！"));
              }
              SysWorkingPeriodEntity nxtCalendar = calendars[0];
              if (nxtCalendar.TimeFrom >= plan_et && plan_et!=DateTime.MinValue && plan_et!=DateTime.MaxValue)
              {
                 // MyDebug.write(" 工序开工时间已超出计划完工时间！");

                  throw (new Exception("工单"+currentWo.WoNo+"工序开工时间已超出计划完工时间！"));
              }

              SetWorkGroupInCalendar(nxtCalendar, workGroupId, nxtCalendar.TimeFrom, plan_et, ref interval, proc, prodIndex, ref schForEachWorkGroup);
          }

      }

      private bool AssignWorkGroups(string workGroupId, DateTime startTime, DateTime endTime, ref int interval, int prodIndex, BomTechnicItemEntity proc, ref List<ProdWoTaskDetailEntity> schForEachWorkGroup)
       {
           ProdWoTaskDetailEntity firstTask = null;
           ProdWoTaskDetailEntity firstTask1 = null;
           string notInWos = "";//排除待排程的工单。

         
           if (startTime >= endTime || interval==0) return true;
           
          //排除待排程工单对资源的占用。
           notInWos += "'" +currentWo.WoNo+ "'";
           if (WaitingScheduleWoList != null)
           {
               foreach(string strWo in WaitingScheduleWoList) notInWos += ", '" + strWo + "'";
           }
           //在工作日历期间，已被占用的工序
           string sql = " select * from prod_wo_task_detail where workgroup_No='" + workGroupId + "' and ((start_time>='" + startTime + "' and start_time<='" + endTime + "') or (end_time>'" + startTime + "' and end_time<='" + endTime + "')) and wo_No not in (" + notInWos + ") order by end_time ";
           List<ProdWoTaskDetailEntity> tasks = DataAccess.Select<ProdWoTaskDetailEntity>(sql);

           //本次已安排给该工作组的任务
           var tasks2 =ppSchedule.GetAssignedTaskInCalendar(workGroupId,startTime,endTime);// (from c in ProductPlanSchedule where c.WorkGroupId == workGroupId && ((c.StartTime >= startTime && c.EndTime < endTime) || (c.EndTime > startTime && c.EndTime <= endTime)) select c).ToList().OrderBy(t => t.EndTime).ToList();

          if (tasks.Count > 0) firstTask = tasks[0];

          if (tasks2.Count > 0)
          {
              if (firstTask == null)
                  firstTask1 = tasks2[0];
              else if (firstTask.StartTime > tasks2[0].StartTime)
              {
                  firstTask1 = tasks2[0];
                  firstTask=null;
              }

          }

          if (firstTask == null && firstTask1 == null)
          {//指定时间范围内该工作组没有分配其它任务
              AssignWorkGroup(workGroupId, startTime, endTime, ref interval, prodIndex, proc.Sequence,proc.TechnicId, ref schForEachWorkGroup);
          }else{//指定时间范围内该工作组已经分配其它任务
              if(  firstTask!=null)
              {
                  if(startTime>=firstTask.StartTime)
                      return AssignWorkGroups(workGroupId,firstTask.EndTime,endTime,ref interval,prodIndex,proc,ref schForEachWorkGroup);
                  else
                  {
                      AssignWorkGroup(workGroupId, startTime, firstTask.StartTime, ref interval, prodIndex, proc.Sequence, proc.TechnicId, ref schForEachWorkGroup);

                      return AssignWorkGroups(workGroupId, firstTask.EndTime, endTime, ref interval, prodIndex, proc, ref schForEachWorkGroup);
                  }
              }else
              {//本次已安排给该工作组的任务
                  if(startTime>=firstTask1.StartTime)
                      return AssignWorkGroups(workGroupId, firstTask1.EndTime, endTime, ref interval, prodIndex, proc, ref schForEachWorkGroup);
                  else
                  {
                      AssignWorkGroup(workGroupId, startTime, firstTask1.StartTime, ref interval, prodIndex, proc.Sequence, proc.TechnicId, ref schForEachWorkGroup);

                      return AssignWorkGroups(workGroupId, firstTask1.EndTime, endTime, ref interval, prodIndex, proc, ref schForEachWorkGroup);
                  }
              }
          }
          return true;

       }


      private void AssignWorkGroup(string workgroupId, DateTime startTime, DateTime endTime, ref int interval, int prodIndex, int sequence, string technicId, ref List<ProdWoTaskDetailEntity> schForEachWorkGroup)
       {
          
           TimeSpan ts = endTime - startTime;
           ProdWoTaskDetailEntity sch = new ProdWoTaskDetailEntity();
           sch.Id = Guid.NewGuid().ToString();
              sch.WoNo=currentWo.WoNo;
              sch.WorkgroupNo=workgroupId;
              sch.Sequence = sequence;
              sch.PlanOrder=currentWo.PoId;
              sch.StartTime=startTime;
              sch.ProdIndex = prodIndex;
              sch.TechnicId = technicId;
              if (ts.TotalMinutes >= interval)
              {
                  sch.EndTime = startTime.AddMinutes(interval);
                  interval = 0;
              }
              else
              {
                  sch.EndTime = endTime;
                  interval = interval -(int) ts.TotalMinutes;
              }
              schForEachWorkGroup.Add(sch);
       }


    }


 
}
